package com.liaoyifan.core.util;

import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowItem;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Sentinel 规则工具类 提供 Sentinel 限流、熔断、热点参数等规则的创建和线程安全的加载方法， 封装了默认配置参数，简化规则定义流程，同时保证多线程环境下规则加载的安全性。
 *
 * @author liaoyifan
 */
@SuppressWarnings("unused")
public class SentinelRule {

    // 默认常量配置 - 流控规则
    /** 默认QPS限流阈值 */
    private static final double DEFAULT_QPS_LIMIT = 200;

    /** 默认线程数限流阈值 */
    private static final int DEFAULT_THREAD_LIMIT = 50;

    // 默认常量配置 - 熔断规则
    /** 默认异常次数 */
    private static final int DEFAULT_EXCEPTION_COUNT = 5;

    /** 默认异常比例阈值 (0-1之间) */
    private static final double DEFAULT_EXCEPTION_RATIO = 0.5;

    /** 默认熔断时间窗口(秒) */
    private static final int DEFAULT_TIME_WINDOW_SEC = 10;

    /** 默认最小请求数（触发熔断的最小请求基数） */
    private static final int DEFAULT_MIN_REQUEST_AMOUNT = 5;

    /** 默认异常统计时间间隔(毫秒) */
    private static final int DEFAULT_STAT_INTERVAL_MS = 10_000;

    /** 默认慢调用统计时间间隔(毫秒) */
    private static final int DEFAULT_SLOW_CALL_STAT_INTERVAL_MS = 60_000;

    /** 默认慢调用比例阈值 (0-1之间) */
    private static final double DEFAULT_SLOW_CALL_RATIO = 0.5;

    // 默认常量配置 - 热点参数规则
    /** 默认热点参数限流阈值 */
    private static final double DEFAULT_PARAM_FLOW_COUNT = 100;

    /** 默认热点参数限流时间窗口(秒) */
    private static final int DEFAULT_PARAM_FLOW_DURATION_SEC = 1;

    /** 规则操作线程安全锁 保证规则加载/更新操作的原子性，避免多线程并发修改导致的规则不一致问题 */
    private static final Lock RULE_LOCK = new ReentrantLock();

    /**
     * 创建QPS流控规则（使用默认阈值） 流控粒度为QPS，当资源的QPS超过设定阈值时触发限流
     *
     * @param resource 资源名称（Sentinel保护的资源标识）
     * @return 配置好的QPS流控规则对象
     */
    public static FlowRule qps(String resource) {
        return qps(resource, DEFAULT_QPS_LIMIT);
    }

    /**
     * 创建QPS流控规则（自定义阈值） 流控粒度为QPS，当资源的QPS超过设定阈值时触发限流
     *
     * @param resource 资源名称（Sentinel保护的资源标识）
     * @param count QPS阈值，超过此值将触发限流
     * @return 配置好的QPS流控规则对象
     */
    public static FlowRule qps(String resource, double count) {
        FlowRule rule = new FlowRule(resource);
        // 设置流控等级为QPS
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        // 设置QPS阈值
        rule.setCount(count);
        return rule;
    }

    /**
     * 创建线程数流控规则（使用默认阈值） 流控粒度为并发线程数，当资源的并发线程数超过设定阈值时触发限流
     *
     * @param resource 资源名称（Sentinel保护的资源标识）
     * @return 配置好的线程数流控规则对象
     */
    public static FlowRule thread(String resource) {
        return thread(resource, DEFAULT_THREAD_LIMIT);
    }

    /**
     * 创建线程数流控规则（自定义阈值） 流控粒度为并发线程数，当资源的并发线程数超过设定阈值时触发限流
     *
     * @param resource 资源名称（Sentinel保护的资源标识）
     * @param count 最大并发线程数阈值，超过此值将触发限流
     * @return 配置好的线程数流控规则对象
     */
    public static FlowRule thread(String resource, int count) {
        FlowRule rule = new FlowRule(resource);
        // 设置流控等级为线程数
        rule.setGrade(RuleConstant.FLOW_GRADE_THREAD);
        // 设置并发线程数阈值
        rule.setCount(count);
        return rule;
    }

    /**
     * 创建异常次数熔断规则（使用默认参数） 当资源的异常请求次数在统计时间内超过阈值时，触发熔断保护
     *
     * @param resource 资源名称（Sentinel保护的资源标识）
     * @return 配置好的异常次数熔断规则对象
     */
    public static DegradeRule count(String resource) {
        return count(resource, DEFAULT_EXCEPTION_COUNT, DEFAULT_TIME_WINDOW_SEC);
    }

    /**
     * 创建异常次数熔断规则（自定义参数） 当资源的异常请求次数在统计时间内超过阈值时，触发熔断保护
     *
     * @param resource 资源名称（Sentinel保护的资源标识）
     * @param count 异常次数阈值，统计时间内超过此次数将触发熔断
     * @param timeWindow 熔断时间窗口(秒)，熔断触发后，资源将在该时间内被降级
     * @return 配置好的异常次数熔断规则对象
     */
    public static DegradeRule count(String resource, int count, int timeWindow) {
        DegradeRule rule = new DegradeRule(resource);
        // 设置熔断等级为异常次数
        rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
        // 设置异常次数阈值
        rule.setCount(count);
        // 设置熔断时间窗口
        rule.setTimeWindow(timeWindow);
        // 设置触发熔断的最小请求数（避免小流量下误触发）
        rule.setMinRequestAmount(DEFAULT_MIN_REQUEST_AMOUNT);
        // 设置异常统计的时间间隔
        rule.setStatIntervalMs(DEFAULT_STAT_INTERVAL_MS);
        return rule;
    }

    /**
     * 创建异常比例熔断规则（使用默认参数） 当资源的异常请求比例超过阈值时，触发熔断保护，在指定时间窗口内停止服务
     *
     * @param resource 资源名称（Sentinel保护的资源标识）
     * @return 配置好的异常比例熔断规则对象
     */
    public static DegradeRule ratio(String resource) {
        return ratio(resource, DEFAULT_EXCEPTION_RATIO, DEFAULT_TIME_WINDOW_SEC);
    }

    /**
     * 创建异常比例熔断规则（自定义参数） 当资源的异常请求比例超过阈值时，触发熔断保护，在指定时间窗口内停止服务
     *
     * @param resource 资源名称（Sentinel保护的资源标识）
     * @param ratio 异常比例阈值 (0-1之间)，如0.5表示50%的请求异常时触发熔断
     * @param timeWindow 熔断时间窗口(秒)，熔断触发后，资源将在该时间内被降级
     * @return 配置好的异常比例熔断规则对象
     */
    public static DegradeRule ratio(String resource, double ratio, int timeWindow) {
        DegradeRule rule = new DegradeRule(resource);
        // 设置熔断等级为异常比例
        rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO);
        // 设置异常比例阈值
        rule.setCount(ratio);
        // 设置熔断时间窗口
        rule.setTimeWindow(timeWindow);
        // 设置触发熔断的最小请求数（避免小流量下误触发）
        rule.setMinRequestAmount(DEFAULT_MIN_REQUEST_AMOUNT);
        // 设置异常统计的时间间隔
        rule.setStatIntervalMs(DEFAULT_STAT_INTERVAL_MS);
        return rule;
    }

    /**
     * 创建慢调用熔断规则（使用默认比例和时间窗） 当资源的慢调用比例超过阈值时，触发熔断保护，在指定时间窗口内停止服务
     *
     * @param resource 资源名称（Sentinel保护的资源标识）
     * @param slowRequestTime 慢调用阈值(毫秒)，超过此时长的请求被视为慢调用
     * @return 配置好的慢调用熔断规则对象
     */
    public static DegradeRule slow(String resource, int slowRequestTime) {
        return slow(resource, slowRequestTime, DEFAULT_SLOW_CALL_RATIO, DEFAULT_TIME_WINDOW_SEC);
    }

    /**
     * 创建慢调用熔断规则（全自定义参数） 当资源的慢调用比例超过阈值时，触发熔断保护，在指定时间窗口内停止服务
     *
     * @param resource 资源名称（Sentinel保护的资源标识）
     * @param slowRequestTime 慢调用阈值(毫秒)，超过此时长的请求被视为慢调用
     * @param ratio 慢调用比例阈值 (0-1之间)，如0.5表示50%的请求为慢调用时触发熔断
     * @param timeWindow 熔断时间窗口(秒)，熔断触发后，资源将在该时间内被降级
     * @return 配置好的慢调用熔断规则对象
     */
    public static DegradeRule slow(
            String resource, int slowRequestTime, double ratio, int timeWindow) {
        DegradeRule rule = new DegradeRule(resource);
        // 设置熔断等级为响应时间（慢调用）
        rule.setGrade(RuleConstant.DEGRADE_GRADE_RT);
        // 设置慢调用阈值（毫秒）
        rule.setCount(slowRequestTime);
        // 设置慢调用比例阈值
        rule.setSlowRatioThreshold(ratio);
        // 设置熔断时间窗口
        rule.setTimeWindow(timeWindow);
        // 设置触发熔断的最小请求数
        rule.setMinRequestAmount(DEFAULT_MIN_REQUEST_AMOUNT);
        // 设置慢调用统计的时间间隔
        rule.setStatIntervalMs(DEFAULT_SLOW_CALL_STAT_INTERVAL_MS);
        return rule;
    }

    /**
     * 创建热点参数规则（使用默认阈值和时间窗） 对指定索引的参数进行限流，适用于单参数热点场景（如用户ID、商品ID等）
     *
     * @param resource 资源名称（Sentinel保护的资源标识）
     * @param paramIdx 参数索引，指定需要限流的参数位置（从0开始）
     * @return 配置好的热点参数规则对象
     */
    public static ParamFlowRule param(String resource, int paramIdx) {
        return param(
                resource,
                paramIdx,
                DEFAULT_PARAM_FLOW_COUNT,
                DEFAULT_PARAM_FLOW_DURATION_SEC,
                Collections.emptyList());
    }

    /**
     * 创建热点参数规则（支持自定义例外项） 对指定索引的参数进行限流，可设置特殊参数值的例外规则
     *
     * @param resource 资源名称（Sentinel保护的资源标识）
     * @param paramIdx 参数索引，指定需要限流的参数位置（从0开始）
     * @param count 限流阈值，单位时间内允许的请求数
     * @param duration 统计时间窗口(秒)
     * @param flowItems 例外项列表，可对特定参数值设置不同的限流阈值
     * @return 配置好的热点参数规则对象
     */
    public static ParamFlowRule param(
            String resource,
            int paramIdx,
            double count,
            int duration,
            List<ParamFlowItem> flowItems) {
        ParamFlowRule rule = new ParamFlowRule(resource);
        // 设置需要限流的参数索引
        rule.setParamIdx(paramIdx);
        // 设置限流阈值
        rule.setCount(count);
        // 设置时间窗口(秒)
        rule.setDurationInSec(duration);
        // 关闭集群模式（单机模式）
        rule.setClusterMode(false);

        // 添加例外项（如某些特殊参数值需要不同的限流策略）
        if (flowItems != null && !flowItems.isEmpty()) {
            rule.setParamFlowItemList(new ArrayList<>(flowItems));
        }
        return rule;
    }

    /**
     * 线程安全的流控规则加载 将新规则添加到现有规则集合中，并原子性更新到Sentinel规则管理器
     *
     * @param rule 需要添加的流控规则
     */
    public static void load(FlowRule rule) {
        // 加锁保证规则更新的线程安全
        RULE_LOCK.lock();
        try {
            // 获取当前已存在的所有流控规则
            List<FlowRule> rules = new ArrayList<>(FlowRuleManager.getRules());
            // 添加新规则
            rules.add(rule);
            // 原子性更新规则（覆盖式更新）
            FlowRuleManager.loadRules(rules);
        } finally {
            // 确保锁释放
            RULE_LOCK.unlock();
        }
    }

    /**
     * 线程安全的熔断规则加载 将新规则添加到现有规则集合中，并原子性更新到Sentinel规则管理器
     *
     * @param rule 需要添加的熔断规则
     */
    public static void load(DegradeRule rule) {
        RULE_LOCK.lock();
        try {
            // 获取当前已存在的所有熔断规则
            List<DegradeRule> rules = new ArrayList<>(DegradeRuleManager.getRules());
            // 添加新规则
            rules.add(rule);
            // 原子性更新规则
            DegradeRuleManager.loadRules(rules);
        } finally {
            RULE_LOCK.unlock();
        }
    }

    /**
     * 线程安全的热点参数规则加载 将新规则添加到现有规则集合中，并原子性更新到Sentinel规则管理器
     *
     * @param rule 需要添加的热点参数规则
     */
    public static void load(ParamFlowRule rule) {
        RULE_LOCK.lock();
        try {
            // 获取当前已存在的所有热点参数规则
            List<ParamFlowRule> rules = new ArrayList<>(ParamFlowRuleManager.getRules());
            // 添加新规则
            rules.add(rule);
            // 原子性更新规则
            ParamFlowRuleManager.loadRules(rules);
        } finally {
            RULE_LOCK.unlock();
        }
    }
}
